/*		
 *      Windows Frame Code Was Published By Jeff Molofee 2000. 
 *      Thanks to the Creator  of Screen Saver of Lesson31_SS. 
 *		Both of You Helped me A Lots.
 *		If You've Found This Code Useful, Please Send Me a Copy At
 *		gan_kim_heng@yahoo.com.
 */

#include <windows.h>		// Header File For Windows
#include <stdio.h>			// Header File For Standard Input/Output
#include <math.h>
#include <gl\gl.h>			// Header File For The OpenGL32 Library
#include <gl\glu.h>			// Header File For The GLu32 Library
#include <scrnsave.h>
#include "resource.h"
#include "myGLUI.h"

#pragma comment( lib, "opengl32.lib")	// Search For OpenGL32.lib While Linking
#pragma comment( lib, "glu32.lib")
#pragma comment( lib, "scrnsave.lib")

HDC			hDC=NULL;		// Private GDI Device Context
HGLRC		hRC=NULL;		// Permanent Rendering Context
HWND		hWnd=NULL;		// Holds Our Window Handle
HINSTANCE	hInstance;		// Holds The Instance Of The Application

DEVMODE			DMsaved;	// Saves the previous screen settings (NEW)
UINT			uTimer;		// timer identifier

bool	keys[256];			// Array Used For The Keyboard Routine
bool	active=TRUE;		// Window Active Flag Set To TRUE By Default
bool	fullscreen=TRUE;	// Fullscreen Flag Set To Fullscreen Mode By Default

const	num=50;				// Number Of Stars To Draw
GLuint	loop;				// General Loop Variable
float	flag=0.0;			// Flag to Create the Sprinkling Effect at the Initiate State 

typedef struct				// Create A Structure For Star
{
	int r, g, b;			// Bubbles Color
	GLfloat x,velox,y,veloy,z,veloz;
}
bubbles;
bubbles bubble[num];		// Need To Keep Track Of 'num' bubbles

GLuint	texture[1];			// Storage For One Texture
void LoadGLTextures();

GLvoid ReSizeGLScene(GLsizei width, GLsizei height)		// Resize And Initialize The GL Window
{
	if (height==0)										// Prevent A Divide By Zero By
	{
		height=1;										// Making Height Equal One
	}

	glViewport(0,0,width,height);						// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix
}

void SetBubbles(int loop)								// Sets The Initial Value Of Each Object (Random)
{
	bubble[loop].r=rand()%256;
	bubble[loop].g=rand()%256;
	bubble[loop].b=rand()%256;

	flag=(float)0.0;									// Reset
	bubble[loop].x=float(sin(rand()/330.0));
	bubble[loop].y=float(-25.0 + rand()/2000.0);
	bubble[loop].z=float(rand()/3000.0);
	bubble[loop].velox=0.0;
	bubble[loop].veloy=0.0;
	bubble[loop].veloz=0.0;
}

int InitGL(GLvoid)										// All Setup For OpenGL Goes Here
{
	glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping 
	glShadeModel(GL_SMOOTH);							// Enable Smooth Shading
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);				// Black Background
	glClearDepth(1.0f);									// Depth Buffer Setup
	
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations
	glBlendFunc(GL_SRC_ALPHA,GL_ONE);					// Set The Blending Function For Translucency
	glEnable(GL_BLEND);
	
	LoadGLTextures();

	for (loop=0; loop<num; loop++)
	{
		SetBubbles(loop);	
	}

	return TRUE;	
}

int DrawGLScene(GLvoid)									// Here's Where We Do All The Drawing
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
	glBindTexture(GL_TEXTURE_2D, texture[0]);

	for (loop=0; loop<num; loop++)						// Loop Through All The bubbles
	{
		glLoadIdentity();								// Reset The View
		glTranslatef(0.0f,0.0f,-50.0f);

		//glTranslatef(float(3*sin(rand()/330.0)),float(rand()/2000.0),float(rand()/3000.0));
		glTranslatef(bubble[loop].x,bubble[loop].y,bubble[loop].z);
		glBindTexture(GL_TEXTURE_2D, texture[0]);

		glColor4ub(bubble[loop].r,bubble[loop].g,bubble[loop].b,255);
		glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
			glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
			glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
			glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
		glEnd();
		
		bubble[loop].velox=float(sin(rand()/1000.0*flag));
		bubble[loop].veloy=float(rand()/40000.0);
		bubble[loop].veloz=float(rand()/30000.0);
		bubble[loop].x+=bubble[loop].velox;
		bubble[loop].y+=bubble[loop].veloy;
		bubble[loop].z+=bubble[loop].veloz;

		flag+=(float)10.0;
		if(flag>1000)
		{
			flag=(float)0.0;
		}

		Sleep(5);

		if (bubble[loop].y>18.0f)						// Is bubble Off The Screen?
		{
			SetBubbles(loop);							// If So, Reassign New Values
		}
	}
	return TRUE;										// Keep Going
}
/************************************************************************************/

GLvoid KillGLWindow(GLvoid)								// Properly Kill The Window
{
	if (hRC)											// Do We Have A Rendering Context?
	{
		if (!wglMakeCurrent(NULL,NULL))					// Are We Able To Release The DC And RC Contexts?
		{
			MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}

		if (!wglDeleteContext(hRC))						// Are We Able To Delete The RC?
		{
			MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}
		hRC=NULL;										// Set RC To NULL
	}

	if (hDC && !ReleaseDC(hWnd,hDC))					// Are We Able To Release The DC
	{
		MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hDC=NULL;										// Set DC To NULL
	}
}

/************************************************************************************/

/*	This Code Creates Our OpenGL Window.  Parameters Are:					*
 *	title			- Title To Appear At The Top Of The Window				*
 *	width			- Width Of The GL Window Or Fullscreen Mode				*
 *	height			- Height Of The GL Window Or Fullscreen Mode			*
 *	bits			- Number Of Bits To Use For Color (8/16/24/32)			*
 *	fullscreenflag	- Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE)	*/
 
BOOL CreateGLWindow(HWND hWnd, int bits)
{
	GLuint		PixelFormat;			// Holds The Results After Searching For A Match
	RECT		WindowRect;				// Grabs Rectangle Upper Left / Lower Right Values
	int width;
	int height;

	hInstance			= GetModuleHandle(NULL);				// Grab An Instance For Our Window

	EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DMsaved); // save the current display state (NEW)

	static	PIXELFORMATDESCRIPTOR pfd=				// pfd Tells Windows How We Want Things To Be
	{
		sizeof(PIXELFORMATDESCRIPTOR),				// Size Of This Pixel Format Descriptor
		1,											// Version Number
		PFD_DRAW_TO_WINDOW |						// Format Must Support Window
		PFD_SUPPORT_OPENGL |						// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,							// Must Support Double Buffering
		PFD_TYPE_RGBA,								// Request An RGBA Format
		bits,										// Select Our Color Depth
		0, 0, 0, 0, 0, 0,							// Color Bits Ignored
		0,											// No Alpha Buffer
		0,											// Shift Bit Ignored
		0,											// No Accumulation Buffer
		0, 0, 0, 0,									// Accumulation Bits Ignored
		16,											// 16Bit Z-Buffer (Depth Buffer)  
		0,											// No Stencil Buffer
		0,											// No Auxiliary Buffer
		PFD_MAIN_PLANE,								// Main Drawing Layer
		0,											// Reserved
		0, 0, 0										// Layer Masks Ignored
	};
	
	if (!(hDC=GetDC(hWnd)))							// Did We Get A Device Context?
	{
		KillGLWindow();								// Reset The Display
		MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;								// Return FALSE
	}

	if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))	// Did Windows Find A Matching Pixel Format?
	{
		KillGLWindow();								// Reset The Display
		MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;								// Return FALSE
	}

	if(!SetPixelFormat(hDC,PixelFormat,&pfd))		// Are We Able To Set The Pixel Format?
	{
		KillGLWindow();								// Reset The Display
		MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;								// Return FALSE
	}

	if (!(hRC=wglCreateContext(hDC)))				// Are We Able To Get A Rendering Context?
	{
		KillGLWindow();								// Reset The Display
		MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;								// Return FALSE
	}

	if(!wglMakeCurrent(hDC,hRC))					// Try To Activate The Rendering Context
	{
		KillGLWindow();								// Reset The Display
		MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;								// Return FALSE
	}

    GetClientRect (hWnd, &WindowRect); 
	
	width = WindowRect.right - WindowRect.left;
	height = WindowRect.bottom - WindowRect.top;
	ReSizeGLScene(width, height);					// Set Up Our Perspective GL Screen


    if (!InitGL())									// Initialize Our Newly Created GL Window
	{
		KillGLWindow();								// Reset The Display
		MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;								// Return FALSE
	}

	return TRUE;									// Success
}

/************************************************************************************/
// (no changes)

LRESULT WINAPI ScreenSaverProc (HWND hWnd,
								UINT uMsg,
								WPARAM wParam,
								LPARAM lParam)
{

	switch (uMsg)									// Check For Windows Messages
	{
        case WM_CREATE:
		{
			if (!CreateGLWindow(hWnd,16))
			{
				return -1;									// Quit If Window Was Not Created
			}

            uTimer = SetTimer(hWnd, 1, 10, NULL); 
			return 0;
		}

        case WM_TIMER: 
			DrawGLScene();                      // Draw Scene
			SwapBuffers(hDC);					// Swap Buffers (Double Buffering)
			break;

        case WM_DESTROY: 
            if (uTimer) 
                KillTimer(hWnd, uTimer); 
			KillGLWindow();									// Kill The Window
			glDeleteTextures(4,texture);
            break; 
	}

	// Pass All Unhandled Messages To DefScreenSaverProc
    return DefScreenSaverProc(hWnd, uMsg, wParam, lParam); 
}

BOOL WINAPI ScreenSaverConfigureDialog (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    return FALSE; 
} 

BOOL WINAPI RegisterDialogClasses(HANDLE hInst)
{ 
    return TRUE; 
} 

int ImageLoadFromResource(LPCTSTR lpszName, Image *image) {
	HANDLE hBmp = LoadImage(hInstance, lpszName, IMAGE_BITMAP,
							LR_DEFAULTSIZE, LR_DEFAULTSIZE, LR_DEFAULTCOLOR);
	if(!hBmp)
		return 0;

	// Get the bitmap dimensions.
	BITMAP bmp;
	if(!GetObject((HBITMAP)hBmp, sizeof(bmp), &bmp))
		return 0;

	// First try to get how much memory we need.
	BITMAPINFO bi = {0};
	bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bi.bmiHeader.biWidth = bmp.bmWidth;
	bi.bmiHeader.biHeight = bmp.bmHeight;
	bi.bmiHeader.biPlanes = bmp.bmPlanes;
	bi.bmiHeader.biBitCount = 24;
	if(!GetDIBits(hDC, (HBITMAP)hBmp, 0, 1, NULL, &bi, DIB_PAL_COLORS))
		return 0;

	// Allocate memory for the required number of bytes. 
	image->sizeX = bi.bmiHeader.biWidth;
	image->sizeY = bi.bmiHeader.biHeight;
	image->data = (char*)malloc(bi.bmiHeader.biSizeImage);

	// Get the bits in 24 bit format.
	if(!GetDIBits(hDC, (HBITMAP)hBmp, 0, bi.bmiHeader.biHeight, (LPVOID)image->data, &bi, DIB_PAL_COLORS))
		return 0;

    for (unsigned long i=0;i<bi.bmiHeader.biSizeImage;i+=3) { /* reverse all of the colors. (bgr -> rgb)*/
	char temp = image->data[i];
	image->data[i] = image->data[i+2];
	image->data[i+2] = temp;
    }

	return 1;
}
/*************************************************************************************/
/***        Load Bitmaps And Convert To Textures                                  ****/
/*************************************************************************************/
void LoadGLTextures() {	
    /* Load Texture*/
    Image *image1;
    
    /* allocate space for texture*/
    image1 = (Image *) malloc(sizeof(Image));
    if (image1 == NULL) {
	printf("Error allocating space for image");
	exit(0);
    }

    if (!ImageLoadFromResource(MAKEINTRESOURCE(IDB_BITMAP1), image1)) {
	exit(1);
    } 
	
    /* Create Texture	*****************************************/
    glGenTextures(2, &texture[0]);
    glBindTexture(GL_TEXTURE_2D, texture[0]);   /* 2d texture (x and y size)*/

    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); /* scale linearly when image bigger than texture*/
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); /* scale linearly when image smalled than texture*/
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_REPEAT);

    /* 2d texture, level of detail 0 (normal), 3 components (red, green, blue), x size from image, y size from image, */
    /* border 0 (normal), rgb color data, unsigned byte data, and finally the data itself.*/
    glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->data);


	free(image1->data);
	free(image1);
};
/*************************************************************************************/
